#include <iostream>
#include <fstream>
#include "program.h"
#include "instruction.h"
#include <algorithm>

using namespace std;


    CProgram::CProgram()
    {

    }

void CProgram::checkForHazards()
{
    CInstruction* lastInstruction;
    CInstruction* currentInstruction;
    unsigned int i;
    for (i = 1; i<instructionList.size(); ++i)
    {
        lastInstruction = instructionList[i-1];
        currentInstruction = instructionList[i];

        unsigned int j = 0;
        while (!lastInstruction->isInstruction())
        {
            lastInstruction = instructionList[i-1-(++j)];
        }

        int lastDest;
        int lastSrc[2];
        lastSrc[0] = 9991;
        lastSrc[1] = 9992;
        lastDest = 9993;

        int currentSrc[2];
        int currentDest;
        currentDest = 9996;
        currentSrc[0] = 9994;
        currentSrc[1] = 9995;

        lastInstruction->getDestReg(&lastDest);
        lastInstruction->getSrcReg(lastSrc);
        currentInstruction->getDestReg(&currentDest);
        currentInstruction->getSrcReg(currentSrc);

        //printf("Now handling:\nC %s: d %d src %d %d \nL %s: d %d src %d %d\n",
        //    currentInstruction->getCode().c_str(), currentDest, currentSrc[0], currentSrc[1],
        //    lastInstruction->getCode().c_str(), lastDest, lastSrc[0], lastSrc[1]);

        bool hazard = false;
        //RAW
        if ((currentSrc[0] == lastDest)||(currentSrc[1] == lastDest))
        {
            printf ("RAW ");
            hazard = true;
        }
        //WAW
        if (currentDest == lastDest)
        {
            if (hazard) printf ("& ");
            printf ("WAW ");
            hazard = true;
        }
        //WAR
        if ((lastSrc[0] == currentDest)||(lastSrc[1] == currentDest))
        {
            if (hazard) printf ("& ");
            printf ("WAR ");
            hazard = true;
        }
        if (hazard)
        {
                printf ("Hazard between %s and %s\n", lastInstruction->getCode().c_str(), currentInstruction->getCode().c_str());
        }
            fflush(stdout);
        }
}


void CProgram::generateInstructions(unsigned int length)
{
	SGlobalInfo info;
	info.instructionCount = length;

	for(unsigned int i = 0; i < length; ++i)
	{
		CInstruction *instr;
		info.currentInstruction = i;
		info.branchAdded = false;

		instr = isa.requestNewInstruction(info);

		if(info.branchAdded == true)
		{
			if(info.branchBackward == false)
			{
				//a forward branch was added
				SLabel l;
				l.name = info.branchName;
				l.position = info.branchPosition;
				l.instruction = new CInstructionLabel(NULL, l.name);
				labelList.push_back(l);

				//cout << "Branch added with name: " << l.name << " to position: " << l.position << endl;
			}
			else
			{
				//backwards branch added
				SLabel l;
				//add branch label
				l.name = info.branchName;
				l.position = info.branchPosition;
				l.instruction = new CInstructionLabel(NULL, l.name);
				labelList.push_back(l);
				//cout << "Backwards branch added with name: " << l.name << " to position: " << l.position << endl;
								
				//add sub before branch
				l.name = "sub instruction";
				l.position = info.currentInstruction;
				l.instruction = new CInstruction3args(NULL, info.regName, 1);
				l.instruction->name = INSTR_SUB_NAME;
				//cout << "Sub instruction added before branch" << endl;
				labelList.push_back(l);

				//add mov instruction in the beginning to fill loopregister
				l.name = "move instruction";
				l.position = 0; //fill the register in the beginning
				l.instruction = new CInstruction2args(NULL, info.regName, info.startValue);
				l.instruction->name = INSTR_MOVE_NAME;
				//cout << "MOV instruction added for branch" << endl;
				labelList.push_back(l);
			}
		}
		instructionList.push_back(instr);
		cout << i << " Adding instruction: " << instr->getCode() << endl;
	}
	//sort the label vector
	stable_sort(labelList.begin(), labelList.end());
	
	for(unsigned int i = 0; i < labelList.size(); ++i)
	{
		//cout << labelList.at(i).position << labelList.at(i).name << endl;
		instructionList.insert(instructionList.begin() + labelList.at(i).position +i, labelList.at(i).instruction);
	}
	labelList.clear();
}

void CProgram::writeToFile(const std::string &filename)
{
	ofstream file;
  	file.open(filename.c_str());

	if(file.is_open())
	{
		cout << "File: " << filename << " opened, writing instructions" << endl;
	}
	else
	{
		cout << "Failed to open file, aborting" << endl;
		return;
	}

	file << isa.getHeaderCode();

	vector<CInstruction*>::iterator iter;
	for(iter = instructionList.begin(); iter != instructionList.end(); ++iter)
	{
		std::string code;
		code = (*iter)->getCode();
		file << code << endl;
		//cout << code << endl;

	}

	file << isa.getFooterCode();
	file.close();
}


